package com.tangcheng.business.sensitive;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.tangcheng.adapter.db.mybatis.plugin.sensitive.annotation.SensitiveField;
import com.tangcheng.adapter.db.mybatis.plugin.sensitive.annotation.SensitiveFieldEncryption;
import com.tangcheng.adapter.db.mybatis.plugin.sensitive.constant.EncryptionTypeEnum;
import com.tangcheng.business.sensitive.entity.addanno.MethodParamsToAddAnnoDesc;
import com.tangcheng.business.sensitive.entity.addanno.MethodToAddAnnoDesc;
import com.tangcheng.business.sensitive.entity.exportdo.AddFieldManualDesc;
import com.tangcheng.business.sensitive.enums.MethodTargetAnnoEnum;
import com.tangcheng.business.sensitive.enums.SensitiveFieldEnums;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 获取敏感字段在mapper层的usages
 * <p>
 * clientMobile
 * masterMobile
 * saleMobile
 */
@Slf4j
public class SearchSensitiveFieldInMapperUtil {
    public static final String LOAD_PREFIX = "/Users/cheng.tang/workspace/codes/mybatis-subject/springboot-mybatis/src/main/java/";
    public static final String ENCRYPTION_TYPE_NAME = "import com.tangcheng.adapter.db.mybatis.plugin.sensitive.constant.EncryptionTypeEnum;";
    public static final String METHOD_NAME = "import com.tangcheng.adapter.db.mybatis.plugin.sensitive.annotation.SensitiveFieldEncryption;";
    public static final String PARAMETER_NAME = "import com.tangcheng.adapter.db.mybatis.plugin.sensitive.annotation.SensitiveField;";
    private static final String rootPackageName = "com.tangcheng";
    //用于收集需要添加加解密注解的方法，key为全限定名
    private static final Map<String, List<MethodToAddAnnoDesc>> methodAddAnnoMap = new HashMap<>();
    //用于收集需要在参数列表添加@SensitiveField注解的方法，key为全限定名
    private static final Map<String, List<MethodParamsToAddAnnoDesc>> methodParamsAddAnnoMap = new HashMap<>();
    //用于收集存在属性需要添加@SensitiveField注解的类，key为全限定名
    private static final Map<String, Set<String>> fieldsAddAnnoMap = new HashMap<>();
    private static final List<AddFieldManualDesc> dtoToAddAnnoManualList = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        Map<String, Pair<String, String>> twoTypes = new HashMap<>();
        twoTypes.put("", new ImmutablePair<>("/Users/cheng.tang/workspace/codes/mybatis-subject/springboot-mybatis/src/main/java/com/tangcheng/adapter/db/user/dao/mapper", "com.tangcheng.adapter.db.user.dao.mapper"));
        twoTypes.put("Biz", new ImmutablePair<>("/Users/cheng.tang/workspace/codes/mybatis-subject/springboot-mybatis/src/main/java/com/tangcheng/adapter/db/user/dao/biz", "com.tangcheng.adapter.db.user.dao.biz"));


        for (SensitiveFieldEnums value : SensitiveFieldEnums.values()) {
            String tableName = value.getTableName();
            for (Map.Entry<String, Pair<String, String>> entry : twoTypes.entrySet()) {
                String mapperName = DataMaskCommonUtils.getMapperName(entry.getKey(), tableName);
                Pair<String, String> path = entry.getValue();
                for (String sensitiveField : value.getFields()) {
                    File[] listFiles = new File(path.getKey()).listFiles(new FilenameFilter() {
                        @Override
                        public boolean accept(File dir, String name) {
                            return Objects.equals(name.split("\\.")[0], mapperName);
                        }
                    });
                    if (listFiles == null) {
                        log.error("listFiles is null");
                        return;
                    }
                    searchField(tableName, DataMaskCommonUtils.humpConv(sensitiveField), Lists.newArrayList(listFiles), path.getValue());
                }
            }
        }
        //去重
        distinctMethodMap(methodAddAnnoMap);
        distinctMethodParamsMap(methodParamsAddAnnoMap);
        //打印
        printCollectMap();
        //添加注解
        addAnnotationTrigger();
        log.info(" dtoToAddAnnoManualList {} ", JSON.toJSONString(dtoToAddAnnoManualList));
        //DataMaskCommonUtils.exportExcel(dtoToAddAnnoManualList, "手动添加字段", "手动添加字段", new FileOutputStream("Add-Anno-Manual" + LocalTime.now().format(DateTimeFormatter.ofPattern("HH+mm+ss")) + ".xls"), AddFieldManualDesc.class);
    }

    /**
     * 打印收集到的需要添加注解的方法、参数、属性
     */
    private static void printCollectMap() {
        //打印收集的需要添加注解的方法信息
        log.info("[addAnnotationOnMethod]===================" + methodAddAnnoMap.size());
        for (Map.Entry<String, List<MethodToAddAnnoDesc>> stringListEntry : methodAddAnnoMap.entrySet()) {
            String className = stringListEntry.getKey();
            stringListEntry.getValue().stream().forEach(new Consumer<MethodToAddAnnoDesc>() {
                @Override
                public void accept(MethodToAddAnnoDesc methodToAddAnnoDesc) {
                    log.info("className:{},method:{},annoType:{}", className, methodToAddAnnoDesc.getMethodName(), methodToAddAnnoDesc.getAnnoType());
                }
            });
        }

        //打印参数列表的收集信息
        log.info("[addAnnotationOnParamsList]===================" + methodParamsAddAnnoMap.size());
        for (Map.Entry<String, List<MethodParamsToAddAnnoDesc>> stringListEntry : methodParamsAddAnnoMap.entrySet()) {

            for (MethodParamsToAddAnnoDesc methodParamsToAddAnnoDesc : stringListEntry.getValue()) {
                log.info("className:{},methodName:{},indexs:{}", stringListEntry.getKey(), methodParamsToAddAnnoDesc.getMethodName(), methodParamsToAddAnnoDesc.getIndexs());
            }
        }

        //用于打印收集到的存在属性需要添加注解的类
        log.info("[addAnnotationOnFields]===================" + fieldsAddAnnoMap.size());
        for (Map.Entry<String, Set<String>> entry : fieldsAddAnnoMap.entrySet()) {
            String className = entry.getKey();
            log.info("className:{},{} is required to add @SensitiveField.", className, entry.getValue());
        }
    }

    /**
     * 调用所有自动添加注解方法
     *
     * @throws Exception
     */
    private static void addAnnotationTrigger() throws Exception {
        //方法上添加注解
        methodAddAnnoHandler(methodAddAnnoMap);

        //方法参数列表添加注解
        methodParamsAddAnnoHandler(methodParamsAddAnnoMap);

        //类属性上添加敏感字段注解
        fieldAddAnnoHandler(fieldsAddAnnoMap);
    }

    /**
     * 方法添加注解去重
     *
     * @param mapper
     */
    private static void distinctMethodMap(Map<String, List<MethodToAddAnnoDesc>> mapper) {
        for (Map.Entry<String, List<MethodToAddAnnoDesc>> stringListEntry : mapper.entrySet()) {
            List<MethodToAddAnnoDesc> newValue = new ArrayList<>();
            Map<String, List<MethodToAddAnnoDesc>> groups = stringListEntry.getValue().stream().collect(Collectors.groupingBy(MethodToAddAnnoDesc::getMethodName));
            for (Map.Entry<String, List<MethodToAddAnnoDesc>> listEntry : groups.entrySet()) {
                Set<MethodTargetAnnoEnum> da = listEntry.getValue().stream().map(item -> item.getAnnoType()).collect(Collectors.toSet());
                MethodToAddAnnoDesc methodToAddAnnoDesc = new MethodToAddAnnoDesc();
                methodToAddAnnoDesc.setMethodName(listEntry.getKey());
                if (da.size() > 1) {
                    //加解密都要
                    methodToAddAnnoDesc.setAnnoType(MethodTargetAnnoEnum.BOTHENCRYDECRY);
                    stringListEntry.setValue(Lists.newArrayList(methodToAddAnnoDesc));
                } else if (da.size() == 1) {
                    //该是什么就是什么
                    methodToAddAnnoDesc.setAnnoType(da.stream().findFirst().get());
                    stringListEntry.setValue(Lists.newArrayList(methodToAddAnnoDesc));
                }
                newValue.add(methodToAddAnnoDesc);
            }
            stringListEntry.setValue(newValue);
        }
    }

    /**
     * 方法参数列表去重
     */
    private static void distinctMethodParamsMap(Map<String, List<MethodParamsToAddAnnoDesc>> mapper) {
        for (Map.Entry<String, List<MethodParamsToAddAnnoDesc>> stringListEntry : mapper.entrySet()) {
            List<MethodParamsToAddAnnoDesc> newValue = new ArrayList<>();
            Map<String, List<MethodParamsToAddAnnoDesc>> groups = stringListEntry.getValue().stream().collect(Collectors.groupingBy(MethodParamsToAddAnnoDesc::getMethodName));

            for (Map.Entry<String, List<MethodParamsToAddAnnoDesc>> listEntry : groups.entrySet()) {
                Set<Integer> argsSet = new HashSet<>();
                listEntry.getValue().forEach(item -> argsSet.addAll(item.getIndexs()));
                MethodParamsToAddAnnoDesc methodParamsToAddAnnoDesc = new MethodParamsToAddAnnoDesc();
                methodParamsToAddAnnoDesc.setMethodName(listEntry.getKey());
                methodParamsToAddAnnoDesc.setIndexs(argsSet);
                newValue.add(methodParamsToAddAnnoDesc);
            }
            stringListEntry.setValue(newValue);
        }
    }

    /**
     * 入口
     *
     * @param tableName       表名，作用：需要判断Example
     * @param targetFieldName 字段名
     * @param files           搜索的文件范围
     * @param packagePrefix   用于Class.forName类加载,与类匹配
     * @throws Exception
     */
    private static void searchField(String tableName, String targetFieldName, List<File> files, String packagePrefix) throws Exception {
        for (File file : files) {
            String className = file.getName().split("\\.")[0];
            Class<?> aClass = Class.forName(packagePrefix + "." + className);
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                boolean paramsBool = handleParams(tableName, packagePrefix, className, declaredMethod, targetFieldName);
                boolean returnBool = handleReturn(declaredMethod, targetFieldName);
                if (paramsBool || returnBool) {
                    MethodToAddAnnoDesc methodToAddAnnoDesc = new MethodToAddAnnoDesc();
                    methodToAddAnnoDesc.setMethodName(declaredMethod.getName());
                    if (paramsBool && returnBool) {
                        methodToAddAnnoDesc.setAnnoType(MethodTargetAnnoEnum.BOTHENCRYDECRY);
                        if (declaredMethod.isAnnotationPresent(SensitiveFieldEncryption.class) && declaredMethod.getAnnotation(SensitiveFieldEncryption.class).value().equals(EncryptionTypeEnum.ENDECRYPT)) {
                            continue;
                        }
                    } else if (paramsBool) {
                        methodToAddAnnoDesc.setAnnoType(MethodTargetAnnoEnum.ENCRY);
                        if (declaredMethod.isAnnotationPresent(SensitiveFieldEncryption.class)) {
                            EncryptionTypeEnum value = declaredMethod.getAnnotation(SensitiveFieldEncryption.class).value();
                            if (value.equals(EncryptionTypeEnum.ENDECRYPT) || value.equals(EncryptionTypeEnum.ENCRYPT)) {
                                continue;
                            }
                        }
                    } else {
                        methodToAddAnnoDesc.setAnnoType(MethodTargetAnnoEnum.DECRY);
                        if (declaredMethod.isAnnotationPresent(SensitiveFieldEncryption.class)) {
                            EncryptionTypeEnum value = declaredMethod.getAnnotation(SensitiveFieldEncryption.class).value();
                            if (value.equals(EncryptionTypeEnum.ENDECRYPT) || value.equals(EncryptionTypeEnum.DECRYPT)) {
                                continue;
                            }
                        }
                    }
                    List<MethodToAddAnnoDesc> newValue = methodAddAnnoMap.getOrDefault(packagePrefix + "." + className, new ArrayList<>());
                    newValue.add(methodToAddAnnoDesc);
                    methodAddAnnoMap.put(packagePrefix + "." + className, newValue);
                }
            }
        }
    }

    /**
     * @param declaredMethod
     * @param targetFieldName
     * @return
     * @throws ClassNotFoundException
     */
    private static boolean handleParams(String tableName, String packagePrefix, String className, Method declaredMethod, String targetFieldName) throws ClassNotFoundException {
        boolean flag = false;

        Parameter[] parameters = declaredMethod.getParameters();
        int index = 0;

        /**
         * 方法的第几个参数需要加注解
         */
        Set<Integer> indexs = new HashSet<>();
        for (Parameter parameter : parameters) {
            if (parameter.getName().contains(targetFieldName) || parameter.getType().getSimpleName().equals(DataMaskCommonUtils.getExampleNameFromTableName(tableName))) {
                flag = true;
                if (!parameter.isAnnotationPresent(SensitiveField.class)) {
                    indexs.add(index);
                }
            }
            Set<String> s = new HashSet<>();
            boolean B = handleField(parameter.getType(), targetFieldName, s);
            if (Boolean.TRUE.equals(B)) {
                flag = true;
                if (!parameter.isAnnotationPresent(SensitiveField.class)) {
                    indexs.add(index);
                }
            }
            Type type = parameter.getParameterizedType();
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                //获取泛型列表
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    Set<String> visitedClasses = new HashSet<>();
                    boolean b = handleField(actualTypeArgument, targetFieldName, visitedClasses);
                    if (Boolean.TRUE.equals(b)) {
                        flag = true;
                        if (!parameter.isAnnotationPresent(SensitiveField.class)) {
                            indexs.add(index);
                        }
                    }
                }
            }
            index++;
        }
        if (!indexs.isEmpty()) {
            MethodParamsToAddAnnoDesc methodParamsToAddAnnoDesc = new MethodParamsToAddAnnoDesc();
            methodParamsToAddAnnoDesc.setMethodName(declaredMethod.getName());
            methodParamsToAddAnnoDesc.setIndexs(indexs);
            List<MethodParamsToAddAnnoDesc> newValue = methodParamsAddAnnoMap.getOrDefault(packagePrefix + "." + className, new ArrayList<>());
            newValue.add(methodParamsToAddAnnoDesc);
            methodParamsAddAnnoMap.put(packagePrefix + "." + className, newValue);
        }
        return flag;
    }

    /**
     * 处理返回值
     *
     * @param declaredMethod
     * @param targetFieldName
     * @return
     * @throws ClassNotFoundException
     */
    private static boolean handleReturn(Method declaredMethod, String targetFieldName) throws ClassNotFoundException {
        boolean flag = false;
        Set<String> s = new HashSet<>();
        boolean B = handleField(declaredMethod.getReturnType(), targetFieldName, s);
        if (Boolean.TRUE.equals(B)) {
            flag = true;
        }
        Type type = declaredMethod.getGenericReturnType();
        if (type instanceof ParameterizedType) {
            ParameterizedType genericReturnType = (ParameterizedType) type;
            Type[] actualTypeArguments = genericReturnType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                Set<String> visitedClasses = new HashSet<>();
                boolean b = handleField(actualTypeArgument, targetFieldName, visitedClasses);
                if (Boolean.TRUE.equals(b)) {
                    flag = true;
                }
            }
        }
        return flag;
    }

    /**
     * 收集的时候只关注当前类
     *
     * @param type
     * @param targetFieldName
     * @param visitedClasses
     * @return
     * @throws ClassNotFoundException
     */
    private static boolean handleField(Type type, String targetFieldName, Set<String> visitedClasses) throws ClassNotFoundException {
        boolean flag = false;
        if (type.getTypeName().startsWith(rootPackageName)) {
            if (visitedClasses.contains(type.getTypeName())) {
                return false;
            }
            visitedClasses.add(type.getTypeName());

            Class tempClass = (Class) type;
            while (Objects.nonNull(tempClass)) {
                Field[] declaredFields = tempClass.getDeclaredFields();
                Set<String> fieldNames = new HashSet<>();
                for (Field field : declaredFields) {
                    if (field.getName().contains(targetFieldName)) {
                        flag = true;
                        if (!field.isAnnotationPresent(SensitiveField.class)) {
                            fieldNames.add(field.getName());
                        }
                    }
                    /**
                     * 存在继承的情况
                     */
                    boolean B = handleField(field.getType(), targetFieldName, visitedClasses);
                    if (Boolean.TRUE.equals(B)) {
                        flag = true;
                        if (!field.isAnnotationPresent(SensitiveField.class)) {
                            fieldNames.add(field.getName());
                        }
                    }
                    Type t = field.getGenericType();
                    if (t instanceof ParameterizedType) {
                        ParameterizedType genericType = (ParameterizedType) t;
                        Type[] actualTypeArguments = genericType.getActualTypeArguments();
                        for (Type actualTypeArgument : actualTypeArguments) {
                            boolean b = handleField(actualTypeArgument, targetFieldName, visitedClasses);
                            if (Boolean.TRUE.equals(b)) {
                                flag = true;
                                if (!field.isAnnotationPresent(SensitiveField.class)) {
                                    fieldNames.add(field.getName());
                                }
                            }
                        }
                    }
                }
                if (!fieldNames.isEmpty()) {
                    Set<String> newValue = fieldsAddAnnoMap.getOrDefault(tempClass.getName(), new HashSet<>());
                    newValue.addAll(fieldNames);
                    fieldsAddAnnoMap.put(tempClass.getName(), newValue);
                }
                tempClass = tempClass.getSuperclass();
            }
        } else {
            //List<List<UserDTO>>
            if (type instanceof ParameterizedType) {
                ParameterizedType t = (ParameterizedType) type;
                Type[] actualTypeArguments = t.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    boolean b = handleField(actualTypeArgument, targetFieldName, visitedClasses);
                    if (Boolean.TRUE.equals(b)) {
                        flag = true;
                    }
                }
            }
        }
        return flag;
    }


    /**
     * 方法上添加注解
     */
    private static void methodAddAnnoHandler(Map<String, List<MethodToAddAnnoDesc>> mapper) throws Exception {
//        String loadPrefix = "src/main/java/";
        for (Map.Entry<String, List<MethodToAddAnnoDesc>> stringListEntry : mapper.entrySet()) {
            Path path = Paths.get(LOAD_PREFIX + stringListEntry.getKey().replace(".", "/") + ".java");
            log.info("[AddAnnotationInMethod] {}", stringListEntry.getKey());
            byte[] bytes = Files.readAllBytes(path);
            String content = new String(bytes, StandardCharsets.UTF_8);
            Pattern compile = Pattern.compile("package (.*?);");
            //System.out.println(content);
            Matcher matcher1 = compile.matcher(content);
            StringBuffer tar = new StringBuffer();
            if (matcher1.find()) {
                //System.out.println("package match");
                matcher1.appendReplacement(tar, matcher1.group() + "\n" + ENCRYPTION_TYPE_NAME + "\n" +
                        METHOD_NAME);
            }
            matcher1.appendTail(tar);
            content = tar.toString();

            for (MethodToAddAnnoDesc methodToAddAnnoDesc : stringListEntry.getValue()) {
                Pattern pattern = Pattern.compile("[\\w|<|>]*\\s*" + Pattern.quote(methodToAddAnnoDesc.getMethodName()) + "\\s*\\(");
                Matcher matcher = pattern.matcher(content);
                StringBuffer modifiedContent = new StringBuffer();
                if (matcher.find()) {
                    if (methodToAddAnnoDesc.getAnnoType().equals(MethodTargetAnnoEnum.ENCRY)) {
                        matcher.appendReplacement(modifiedContent, "@SensitiveFieldEncryption(EncryptionTypeEnum.ENCRYPT)\n\t" + matcher.group());
                    } else if (methodToAddAnnoDesc.getAnnoType().equals(MethodTargetAnnoEnum.DECRY)) {
                        matcher.appendReplacement(modifiedContent, "@SensitiveFieldEncryption(EncryptionTypeEnum.DECRYPT)\n\t" + matcher.group());
                    } else if (methodToAddAnnoDesc.getAnnoType().equals(MethodTargetAnnoEnum.BOTHENCRYDECRY)) {
                        matcher.appendReplacement(modifiedContent, "@SensitiveFieldEncryption(EncryptionTypeEnum.ENDECRYPT)\n\t" + matcher.group());
                    }
                    matcher.appendTail(modifiedContent);
                    content = modifiedContent.toString();
                }
            }
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * 方法参数列表添加注解
     */
    private static void methodParamsAddAnnoHandler(Map<String, List<MethodParamsToAddAnnoDesc>> mapper) throws IOException {
//        String loadPrefix = "src/main/java/";
        String loadPrefix = LOAD_PREFIX;
        for (Map.Entry<String, List<MethodParamsToAddAnnoDesc>> stringListEntry : mapper.entrySet()) {
            Path path = Paths.get(loadPrefix + stringListEntry.getKey().replace(".", "/") + ".java");
            log.info("[AddAnnotationInParamsList] {}", stringListEntry.getKey());
            byte[] bytes = Files.readAllBytes(path);
            String content = new String(bytes, StandardCharsets.UTF_8);

            Pattern packageCompile = Pattern.compile("package (.*?);");
            //System.out.println(content);
            Matcher packageMatcher = packageCompile.matcher(content);
            StringBuffer tar = new StringBuffer();
            if (packageMatcher.find()) {
                //System.out.println("package match");
                packageMatcher.appendReplacement(tar, packageMatcher.group() + "\n" + PARAMETER_NAME);
            }
            packageMatcher.appendTail(tar);
            content = tar.toString();

            for (MethodParamsToAddAnnoDesc methodParamsToAddAnnoDesc : stringListEntry.getValue()) {// public List<Integer> m(String abc, )
                Pattern pattern = Pattern.compile("([\\w|<|>]*\\s*" + Pattern.quote(methodParamsToAddAnnoDesc.getMethodName()) + "\\s*\\()" + Pattern.compile("(.*?)\\);"), Pattern.DOTALL);
                Matcher matcher = pattern.matcher(content);
                StringBuffer modifiedContent = new StringBuffer();
                if (matcher.find()) {
                    String paramsList = matcher.group(2);
                    String[] split = paramsList.split(",");
                    for (Integer idx : methodParamsToAddAnnoDesc.getIndexs()) {
                        //TODO check加注解的情况
                        if (split[idx].contains(")")) {
                            Pattern compile = Pattern.compile("\\)");
                            Matcher matcher1 = compile.matcher(split[idx]);
                            if (matcher1.find()) {
                                split[idx] = split[idx].substring(0, matcher1.start() + 1) + " @SensitiveField " + split[idx].substring(matcher1.start() + 1);
                            }
                        } else {
                            StringBuilder sb = new StringBuilder();
                            sb.append("@SensitiveField ");
                            sb.append(split[idx]);
                            split[idx] = sb.toString();
                        }
                    }
                    matcher.appendReplacement(modifiedContent, matcher.group(1) + String.join(",", split) + ");");
                }
                matcher.appendTail(modifiedContent);
                content = modifiedContent.toString();
            }
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * 类属性添加注解
     * <p>
     * public String userName;
     * <p>
     * public List<Integer> mobiles;
     */
    private static void fieldAddAnnoHandler(Map<String, Set<String>> mapper) throws IOException {
//        String loadPrefix = "src/main/java/";
        String loadPrefix = LOAD_PREFIX;
        Set<String> classesNotInCurrentProjectSet = new HashSet<>();
        for (Map.Entry<String, Set<String>> entry : mapper.entrySet()) {
            log.info("[AddAnnotationInField] {}", entry.getKey());
            Path path = Paths.get(loadPrefix + entry.getKey().replace(".", "/") + ".java");
            byte[] bytes = null;
            try {
                bytes = Files.readAllBytes(path);
            } catch (Exception e) {
                log.warn("[AddAnnotationInField] error:", e);
                classesNotInCurrentProjectSet.add(entry.getKey());
                AddFieldManualDesc addFieldManualDesc = new AddFieldManualDesc();
                addFieldManualDesc.setClassName(entry.getKey());
                addFieldManualDesc.setFieldSet(entry.getValue());
                addFieldManualDesc.setSize(entry.getValue().size());

                dtoToAddAnnoManualList.add(addFieldManualDesc);
                continue;
            }
            String content = new String(bytes, StandardCharsets.UTF_8);
            Pattern packageCompile = Pattern.compile("package (.*?);");
            Matcher packageMatcher = packageCompile.matcher(content);
            StringBuffer tar = new StringBuffer();
            if (packageMatcher.find()) {
                packageMatcher.appendReplacement(tar, packageMatcher.group() + "\n" + PARAMETER_NAME);
            }
            packageMatcher.appendTail(tar);
            content = tar.toString();

            StringBuffer sb = new StringBuffer();
            for (String fieldName : entry.getValue()) {
                Pattern compile = Pattern.compile("((?:public|private|protected|static|final|transient|volatile)\\s+)*([a-zA-Z_$][a-zA-Z0-9<>_$\\[\\]]*)\\s+" + Pattern.quote(fieldName) + "\\s*(=\\s*[^;]*)*;");
                Matcher matcher = compile.matcher(content);
                if (matcher.find()) {
                    matcher.appendReplacement(sb, "@SensitiveField\n\t" + matcher.group());
                }
                matcher.appendTail(sb);
                content = sb.toString();
                sb.setLength(0);
            }
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
        }
        log.info("totalSize {},following classes are required to add annotation by hand:{}", classesNotInCurrentProjectSet.size(), classesNotInCurrentProjectSet);
    }
}
